section .text
[bits 32]

global int00_handler
global int01_handler
global int02_handler
global int03_handler
global int04_handler
global int05_handler
global int06_handler
global int07_handler
global int08_handler
global int09_handler
global int0a_handler
global int0b_handler
global int0c_handler
global int0d_handler
global int0e_handler
global int0f_handler

global main_isr
global syscall_entry
global new_cr3
global old_cr3
global syscall_ret

global splx
global splhi

global copyin
global copyout
global copyinstr
global known_fault1
global known_fault2
global known_fault3

extern int_handler
extern sys_tss
extern curth
extern syscall_dispatch

; timer int
int00_handler:
    mov [irqn],dword 0x00
    jmp main_isr

; keyboard
int01_handler:
    mov [irqn],dword 0x01
    jmp main_isr

; cascade PICs
int02_handler:
    mov [irqn],dword 0x02
    jmp main_isr

; COM2
int03_handler:
    mov [irqn],dword 0x03
    jmp main_isr

; COM1
int04_handler:
    mov [irqn],dword 0x04
    jmp main_isr

; LPT2
int05_handler:
    mov [irqn],dword 0x05
    jmp main_isr

; Floppy
int06_handler:
    mov [irqn],dword 0x06
    jmp main_isr

; LPT1
int07_handler:
    mov [irqn],dword 0x07
    jmp main_isr

; RTC (real time clock)
int08_handler:
    mov [irqn],dword 0x08
    jmp main_isr

; Redirect IRQ2
int09_handler:
    mov [irqn],dword 0x09
    jmp main_isr

; IRQ10
int0a_handler:
    mov [irqn],dword 0x0a
    jmp main_isr

; IRQ11
int0b_handler:
    mov [irqn],dword 0x0b
    jmp main_isr

; PS/2 mouse
int0c_handler:
    mov [irqn],dword 0x0c
    jmp main_isr

; CoProcessor
int0d_handler:
    mov [irqn],dword 0x0d
    jmp main_isr

; Primary IDE controller
int0e_handler:
    mov [irqn],dword 0x0e
    jmp main_isr

; Secondary IDE controller
int0f_handler:
    mov [irqn],dword 0x0f
    jmp main_isr


; main interrupt routine
main_isr:
    cli
    cld
    
    pushad                      ; save all regs
    push ds
    push es
    push fs
    push gs

    mov ax,16
    mov ds,ax
    mov es,ax
    mov fs,ax
    mov gs,ax


    xor eax,eax
    mov eax,[curth]		; get ptr of current thread
    cmp eax,0               	; check ptr for zero!
    je err
    mov [eax],esp		; backup current esp value of thread

    xor eax,eax
    mov eax,cr3                 ; backup current CR3
    mov [old_cr3],eax

    push dword esp              ; push stack0 ptr
    push dword [irqn]           ; push irq num
    call int_handler
    cmp eax,0               	; check return value (context_t)
    je err                      ; error found

    mov [ctx],eax               ; backup ctx ptr
    mov ebx,[old_cr3]           ; compare new pdir & old
    mov eax,[new_cr3]
    cmp eax,ebx
    je not_change_cr3           ; the same pdir
    mov eax,[new_cr3]		; loading new pdir 
    mov cr3,eax
    jmp patch_tss               ; patching system TSS

not_change_cr3:
    mov eax,[ctx]		; get ptr to ctx.stack0 of new thread
    mov ebx,[eax]               ; get new esp0 value (kstack_top)
    mov esp,ebx                 ; new stack ptr

patch_tss:                     
    mov eax,[ctx]               ; get context addr  (FIXED)
    mov ecx,[eax+4]             ; get kstack
    mov [sys_tss+4],ecx        	; patch sys_tss

    pop gs                      ; restore all regs
    pop fs
    pop es
    pop ds

    popad
    iretd                   	; and switch to new task

err:
    cli
    hlt
        

syscall_entry:
    cld
    
    pushad                      ; save all regs
    push ds
    push es
    push fs
    push gs

    mov ax,16
    mov ds,ax
    mov es,ax
    mov fs,ax
    mov gs,ax
    
    call syscall_dispatch
    mov eax, [esp+10]		; return value

syscall_ret:
    pop gs                      ; restore all regs
    pop fs
    pop es
    pop ds

    popad
    iretd                   	; and switch to new task

splhi:
    cli
    mov eax, [curspl]
    mov [curspl], dword 15
    ret
    
splx:
    cli
    mov eax, [esp+4]
    mov [curspl], eax
    cmp eax, 0
    ja no
    sti
no:
    ret

; *
; * Copy data from user to kernel space.
; * Returns 0 on success, or EFAULT on page fault.
; *
; *  syntax - int copyin(const void *uaddr, void *kaddr, size_t len)
; *
	;.global known_fault1
copyin:
    push esi
    push edi
    push dword (-1)

    mov esi, [esp+16]
    mov edi, [esp+20]
    mov ecx, [esp+24]

    mov edx, esi	; check if valid user addr
    add edx, ecx
    jc copy_fault
    cmp edx, 0xe0000000	; user area?
    jae copy_fault
    cld

known_fault1:	; may be fault here!
    rep movsb

    pop	eax
    xor eax, eax	; set no error
    pop	edi
    pop esi
    ret

;*
;* Copy data to user from kernel space.
;* Returns 0 on success, or EFAULT on page fault.
;*
;*  syntax - int copyout(const void *kaddr, void *uaddr, size_t len)
;*
;	.global known_fault2
copyout:
    push esi
    push edi
    push dword (-1)	; Set EFAULT as default return

    mov esi, [esp+16]
    mov edi, [esp+20]
    mov ecx, [esp+24]

    mov edx, edi
    add edx, ecx
    jc	copy_fault
    cmp edx, 0xe0000000		; User area?
    jae copy_fault
    cld

known_fault2:		; May be fault here
    rep	movsb

    pop eax
    xor eax, eax	; Set no error
    pop edi
    pop esi
    ret

;*
;* copyinstr - Copy string from user space.
;* Returns 0 on success, or EFAULT on page fault, or ENAMETOOLONG.
;*
;*  syntax - int copyinstr(const char *uaddr, void *kaddr, size_t len);
;*
;* Note: The returned length value does NOT include the NULL terminator.
;*
;	.global known_fault3
copyinstr:
    push esi
    push edi
    push dword (-1)	; Set EFAULT as default return

    mov esi, [esp+16]
    mov edi, [esp+20]
    mov ecx, [esp+24]
    
    mov edx, esi
    add edx, ecx
    jc copy_fault
    cmp edx, 0xe0000000 	; User area?
    jae copy_fault
    cld
    jmp second

first:
    dec ecx
    jz copyin_toolong

second:
known_fault3:				; May be fault here
    lodsb
    stosb
    test al, al
    jnz	first

    pop eax
    xor eax, eax		; Set no error
    pop edi
    pop esi
    ret

copyin_toolong:
    pop eax
    mov eax, (-2)
    pop edi
    pop esi
    ret

;*
;* Fault entry for user access
;*
copy_fault:
	pop eax			; Get return value from stack
	pop edi
	pop esi
	ret

section .data

irqn:       dd  0           	; irq's number for main isr
old_cr3:    dd  0
new_cr3:    dd  0
ctx:        dd  0

section .bss

curspl:	    dd	0
